Port os_hardening to Rust, and let a kernel-only capture answer - #24
Merged
Merged
Conversation
The engine reads the kernel's hardening report; until the CLI does too, an
operator working offline gets "no U-Boot session found; nothing was assessed" on
a capture that plainly states its KASLR is off because the bootloader supplied no
seed.
Three kernel-stage fixtures now sit in the shared expectation, so the two
implementations cannot drift on this the way they could have on boot_integrity:
selinux-ignored.log bootintel-6 lines 145-155. The trap: `selinux=0` listed
under `Unknown command line parameters:`, which means
the kernel IGNORED it and SELinux is not compiled in,
rather than switched off.
kernel-hardening.log bootintel-21 lines 38-115. KASLR on, memory init off,
and an LSM list of `capability,integrity` that contains
no mandatory access control at all.
kaslr-no-seed.log bootintel-27 lines 148-234. The embedded failure mode,
where the kernel wanted to randomise and the bootloader
handed it no entropy.
Both sides reproduce the expectation byte for byte across eight fixtures now,
five of them real captures.
Behaviour change worth reading: `verdict` used to exit 3 whenever there was no
U-Boot session. That was right when the session was the only thing it assessed
and became wrong the moment a plain boot log could produce a real answer.
Exiting 3 with "nothing was assessed" on a capture that just reported its
hardening posture is false, and a CI job keyed on that code would treat an answer
as a failure to answer. It now exits 3 only when the capture yields neither, and
a capture with neither is tested.
No findings are raised on the Rust side, deliberately: the detector sets are
pinned at 14 labels across Rust and the browser library, and a fifteenth would
break that parity. The engine raises the findings; both sides share the facts.
355 tests, clippy clean under -D warnings, rustfmt clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zenofex
added a commit
that referenced
this pull request
Sep 28, 2026
Version bump, lockfile, changelog. No code changes: everything here is already on `main` and was reviewed in #24. ## What ships `bootintel verdict` answers for captures that never reach a U-Boot prompt: ``` no U-Boot session in boot.log kernel hardening memory init stack:off heap alloc:off heap free:off KASLR disabled (lack of seed) LSM capability, integrity (none provide mandatory access control) ``` Mandatory access control, memory initialisation and kernel address randomisation, read from what the kernel itself announced. Ported from the engine and pinned against it by three kernel-stage fixtures. ## Why MINOR Either reason alone is enough: - New behaviour. - **Changed contract.** `verdict` used to exit 3 whenever there was no U-Boot session; it now exits 3 only when a capture yields neither a session nor a hardening posture. The old behaviour became wrong the moment a plain boot log could produce a real answer — a CI job keyed on that code would have treated an answer as a failure to answer. A capture with neither still exits 3, and that case is tested. ## Verification - `cargo test --workspace`: 364 passed, 0 failed - `clippy --workspace --all-targets -- -D warnings` and `fmt --all --check`: clean - `cargo build --release` then `bootintel --version` reports `bootintel 0.10.0` - Both implementations reproduce the shared expectation byte for byte across eight fixtures, five of them real captures Both version bumps landed first try — third release running for `docs/releasing.md` step 1, which exists because the `bootintel-detectors` pin is not derived by cargo. After merge: dispatch `cli-release` for `0.10.0` with `publish_crates`, verify the draft against `SHA256SUMS`, publish and mark latest, then move the tap (step 7) and sync the in-repo reference copy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The engine reads the kernel's hardening report (BootIntel.com@5508944, expectation at 0a426e1e). Until the CLI does too, an operator working offline gets "no U-Boot session found; nothing was assessed" on a capture that plainly states its KASLR is off because the bootloader supplied no seed.
Three kernel-stage fixtures, all real captures
selinux-ignored.logselinux=0underUnknown command line parameters:— the kernel ignored it, so SELinux is not compiled in rather than switched offkernel-hardening.logcapability,integritywith no mandatory access controlkaslr-no-seed.logBoth implementations reproduce
expect.txtbyte for byte across eight fixtures now, five of them real captures.The distinctions that decide whether the output is trustworthy
selinux=0means opposite things depending on the line it sits on. Reporting the ignored-parameter case as "disabled by boot parameter" would describe a device that does not exist, while understating a worse fact.capabilityin the LSM list is not access control.lsm=capability,integrityreports no MAC;lsm=capability,yama,apparmorcorrectly reports AppArmor.mac_modulesrenders even when empty, because "an LSM line was seen and none provide MAC" is a different claim from "no LSM line was seen". A renderer collapsing the two would hide exactly the divergence the expectation exists to catch.Behaviour change worth reading
verdictused to exit 3 whenever there was no U-Boot session. That was right when the session was the only thing it assessed, and became wrong the moment a plain boot log could produce a real answer — "nothing was assessed" would be false, and a CI job keyed on that code would treat an answer as a failure to answer.It now exits 3 only when the capture yields neither a session nor a posture. A capture with neither is tested.
No findings are raised on the Rust side, deliberately: the detector sets are pinned at 14 labels across Rust and the browser library, and a fifteenth would break that parity. The engine raises the findings; both sides share the facts.
Verification
cargo test --workspace: 355 passed, 0 failedclippy --workspace --all-targets -- -D warningsandfmt --all --check: clean🤖 Generated with Claude Code